
#include "RtBase.h"
#include "RtConnectorWrapper.h"
#include "RtTimeValue.h"
#include "RtTransportBase.h"
#include "RtConnectorTcpT.h"
#include "RtConnectorUdpT.h"
#include "RtTransportTcp.h"
#include "RtTransportUdp.h"
#include "RtConnectorProxyT.h"
#ifdef RT_SUPPORT_OPENSSL
#include "RtConnectorOpenSslT.h"
#endif // RT_SUPPORT_OPENSSL


CRtConnectorWrapper::CRtConnectorWrapper()
	: m_pReactor(NULL)
	, m_pSink(NULL)
	, m_pConnector(NULL)
	, m_bClosed(TRUE)
{
}

CRtConnectorWrapper::~CRtConnectorWrapper()
{
	Close_i();
	delete m_pConnector;
}

DWORD CRtConnectorWrapper::AddReference()
{
	return CRtReferenceControlSingleThread::AddReference();
}

DWORD CRtConnectorWrapper::ReleaseReference()
{
	return CRtReferenceControlSingleThread::ReleaseReference();
}

RtResult CRtConnectorWrapper::Init(CRtConnectionManager::CType aType)
{
	RT_ASSERTE_RETURN(!m_pConnector, RT_ERROR_ALREADY_INITIALIZED);

	RT_ASSERTE(!m_pReactor);
	m_pReactor = CRtThreadManager::Instance()->GetThreadReactor(CRtThreadManager::TT_NETWORK);
	RT_ASSERTE_RETURN(m_pReactor, RT_ERROR_NOT_INITIALIZED);

	switch(aType) {
	case CRtConnectionManager::CTYPE_TCP:
		m_pConnector = new 
			CRtConnectorTcpT<CRtConnectorWrapper, CRtTransportTcp, CRtSocketStream>
			(m_pReactor, *this);
		break;

	case CRtConnectionManager::CTYPE_UDP:
		m_pConnector = new
			CRtConnectorUdpT<CRtConnectorWrapper, CRtTransportUdp, CRtSocketDgram>
			(m_pReactor, *this);
		break;

#ifdef RT_SUPPORT_OPENSSL
	case CRtConnectionManager::CTYPE_SSL_DIRECT:
	case CRtConnectionManager::CTYPE_SSL_WITH_BROWER_PROXY:
	case CRtConnectionManager::CTYPE_SSL:
		m_pConnector = new 
			CRtConnectorOpenSslT<CRtConnectorWrapper>(m_pReactor, *this, aType);
		break;
#endif // RT_SUPPORT_OPENSSL

	case CRtConnectionManager::CTYPE_TCP_WITH_BROWER_PROXY:
		m_pConnector = new 
			 CRtConnectorProxyT<CRtConnectorWrapper, CRtTransportTcp, CRtSocketStream>(m_pReactor, *this, /*FALSE*/TRUE);//jobs, for tcp proxy, 2006/03/21
		break;
		
	default:
		RT_ERROR_TRACE_THIS("CRtConnectorWrapper::Init, error type=" << aType);
		Close_i();
		return RT_ERROR_INVALID_ARG;
	}

	if (!m_pConnector) {
		Close_i();
		return RT_ERROR_OUT_OF_MEMORY;
	}
	else {
		return RT_OK;
	}
}

void CRtConnectorWrapper::
AsycConnect(IRtAcceptorConnectorSink* aSink, const CRtInetAddr& aAddrPeer, 
			CRtTimeValue* aTimeout, CRtInetAddr *aAddrLocal)
{
	RT_ASSERTE(m_pConnector);

	m_pSink = aSink;
	RT_ASSERTE(m_pSink);

	RT_ASSERTE(m_bClosed);
	m_bClosed = FALSE;

//	RT_INFO_TRACE_THIS("CRtConnectorWrapper::AsycConnect,"
//		" addr=" << aAddrPeer.GetIpDisplayName() <<
//		" port=" << aAddrPeer.GetPort() << 
//		" m_pConnector=" << m_pConnector);

	int nRet = -1;
	if (m_pConnector && m_pSink)
		nRet = m_pConnector->Connect(aAddrPeer, aAddrLocal);
	if (nRet == -1) {
		RT_WARNING_TRACE_THIS("CRtConnectorWrapper::AsycConnect, connect failed."
			" addr=" << aAddrPeer.GetIpDisplayName() <<
			" err=" << RtGetSystemErrorInfo(errno));
		m_pReactor->ScheduleTimer(
			this, 
			reinterpret_cast<LPVOID>(RT_ERROR_NETWORK_CONNECT_ERROR), 
			CRtTimeValue(0, 0), 
			1);
		return;
	}

	if (aTimeout) {
		m_pReactor->ScheduleTimer(
			this, 
			reinterpret_cast<LPVOID>(RT_ERROR_NETWORK_CONNECT_TIMEOUT), 
			*aTimeout, 
			1);
	}
}

int CRtConnectorWrapper::
OnConnectIndication(RtResult aReason, IRtTransport *aTrpt, IRtConnectorInternal *aId)
{
	RT_ASSERTE(m_pConnector);
	RT_ASSERTE(aId == m_pConnector);
	RT_ASSERTE_RETURN(m_pSink, 0);

//	RT_INFO_TRACE_THIS("CRtConnectorWrapper::OnConnectIndication,"
//		" aReason=" << aReason <<
//		" aTrpt=" << aTrpt <<
//		" aId=" << aId);

	CRtAutoPtr<IRtTransport> pTransport(aTrpt);
	if (RT_FAILED(aReason)) {
		Close_i();
		m_pSink->OnConnectIndication(aReason, NULL, this);
	}
	else {
		Close_i();
		m_pSink->OnConnectIndication(aReason, aTrpt, this);
	}
	return 0;
}

void CRtConnectorWrapper::CancelConnect()
{
	Close_i();
	m_pSink = NULL;
}

void CRtConnectorWrapper::Close_i()
{
	//RT_INFO_TRACE_THIS("CRtConnectorWrapper::Close_i, m_bClosed="<<m_bClosed);
	if (m_bClosed)
		return;
	m_bClosed = TRUE;
	
	// Don't cleanup resources due to connect again.
	if (m_pConnector) {
		m_pConnector->Close();
//		delete m_pConnector;
//		m_pConnector = NULL;
	}
	if (m_pReactor) {
		m_pReactor->CancelTimer(this);
//		m_pReactor = NULL;
	}

	// can't empty m_pSink because callback follows Close_i()
//	m_pSink = NULL;
}

void CRtConnectorWrapper::OnTimeout(const CRtTimeValue &aCurTime, LPVOID aArg)
{
	RT_ASSERTE(m_pSink);
	
	RtResult rvReason = reinterpret_cast<long>(aArg);
	if (rvReason == RT_ERROR_NETWORK_CONNECT_ERROR) {
		RT_INFO_TRACE_THIS("CRtConnectorWrapper::OnTimeout, connect failed.");
	}
	else if (rvReason == RT_ERROR_NETWORK_CONNECT_TIMEOUT) {
		RT_INFO_TRACE_THIS("CRtConnectorWrapper::OnTimeout, connect timeout.");
	}
	else {
		RT_ERROR_TRACE_THIS("CRtConnectorWrapper::OnTimeout, unkown nReason=" << rvReason);
		RT_ASSERTE(FALSE);
		return;
	}

	Close_i();
	m_pSink->OnConnectIndication(rvReason, NULL, this);
}

BOOL CRtConnectorWrapper::IsConnector()
{
	return TRUE;
}
